#include "sdk.h"
#include "wx/module.h"
#include "wx/display.h"
#include "wx/bookctrl.h"
#include "scrollingdialog.h"

#define wxEXTRA_DIALOG_HEIGHT 30

IMPLEMENT_CLASS( wxDialogLayoutAdapter, wxObject )



wxDialogLayoutAdapter* wxDialogHelper::sm_layoutAdapter = NULL;
bool wxDialogHelper::sm_layoutAdaptation = true;

void wxDialogHelper::Init() {
  m_layoutAdaptationLevel = 3;
  m_layoutLayoutAdaptationDone = FALSE;
}


bool wxDialogHelper::DoLayoutAdaptation() {
  if( GetLayoutAdapter() ) {
    return GetLayoutAdapter()->DoLayoutAdaptation( this );
  } else {
    return false;
  }
}


bool wxDialogHelper::CanDoLayoutAdaptation() {
  return ( GetLayoutAdaptation() && !m_layoutLayoutAdaptationDone && GetLayoutAdaptationLevel() != 0 && GetLayoutAdapter() != NULL && GetLayoutAdapter()->CanDoLayoutAdaptation( this ) );
}

wxDialogLayoutAdapter* wxDialogHelper::SetLayoutAdapter( wxDialogLayoutAdapter* adapter ) {
  wxDialogLayoutAdapter* oldLayoutAdapter = sm_layoutAdapter;
  sm_layoutAdapter = adapter;
  return oldLayoutAdapter;
}



IMPLEMENT_CLASS( wxStandardDialogLayoutAdapter, wxDialogLayoutAdapter )


bool wxStandardDialogLayoutAdapter::CanDoLayoutAdaptation( wxDialogHelper* dialog ) {
  if( dialog->GetDialog()->GetSizer() ) {
    wxSize windowSize, displaySize;
    return MustScroll( dialog->GetDialog(), windowSize, displaySize ) != 0;
  } else {
    return false;
  }
}

bool wxStandardDialogLayoutAdapter::DoLayoutAdaptation( wxDialogHelper* dialog ) {
  if( dialog->GetDialog()->GetSizer() ) {
    wxBookCtrlBase* bookContentWindow = wxDynamicCast( dialog->GetContentWindow(), wxBookCtrlBase );
    if( bookContentWindow ) {
      wxWindowList windows;
      for( size_t i = 0; i < bookContentWindow->GetPageCount(); i++ ) {
        wxWindow* page = bookContentWindow->GetPage( i );
        wxScrolledWindow* scrolledWindow = wxDynamicCast( page, wxScrolledWindow );
        if( scrolledWindow ) {
          windows.Append( scrolledWindow );
        } else if( !scrolledWindow && page->GetSizer() ) {
          scrolledWindow = new wxScrolledWindow( page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL | wxBORDER_NONE );
          wxSizer* oldSizer = page->GetSizer();
          wxSizer* newSizer = new wxBoxSizer( wxVERTICAL );
          newSizer->Add( scrolledWindow, 1, wxEXPAND, 0 );
          page->SetSizer( newSizer, false );
          scrolledWindow->SetSizer( oldSizer );
          ReparentControls( page, scrolledWindow, NULL );
          windows.Append( scrolledWindow );
        }
      }
      FitWithScrolling( dialog->GetDialog(), windows );
    } else {
      wxScrolledWindow* scrolledWindow = new wxScrolledWindow( dialog->GetDialog(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL | wxBORDER_NONE );
      int buttonSizerBorder = 0;
      wxSizer* buttonSizer = FindButtonSizer( true, dialog, dialog->GetDialog()->GetSizer(), buttonSizerBorder );
      if( !buttonSizer && dialog->GetLayoutAdaptationLevel() > 1 ) {
        buttonSizer = FindButtonSizer( false, dialog, dialog->GetDialog()->GetSizer(), buttonSizerBorder );
      }
      if( !buttonSizer && dialog->GetLayoutAdaptationLevel() > 2 ) {
        int count = 0;
        wxStdDialogButtonSizer* stdButtonSizer = new wxStdDialogButtonSizer;
        buttonSizer = stdButtonSizer;
        FindLooseButtons( dialog, stdButtonSizer, dialog->GetDialog()->GetSizer(), count );
        if( count > 0 ) {
          stdButtonSizer->Realize();
        } else {
          delete buttonSizer;
          buttonSizer = NULL;
        }
      }
      if( buttonSizerBorder == 0 ) {
        buttonSizerBorder = 5;
      }
      ReparentControls( dialog->GetDialog(), scrolledWindow, buttonSizer );
      wxBoxSizer* newTopSizer = new wxBoxSizer( wxVERTICAL );
      wxSizer* oldSizer = dialog->GetDialog()->GetSizer();
      dialog->GetDialog()->SetSizer( newTopSizer, false );
      newTopSizer->Add( scrolledWindow, 1, wxEXPAND | wxALL, 0 );
      if( buttonSizer ) {
        newTopSizer->Add( buttonSizer, 0, wxEXPAND | wxALL, buttonSizerBorder );
      }
      scrolledWindow->SetSizer( oldSizer );
      FitWithScrolling( dialog->GetDialog(), scrolledWindow );
    }
  }
  dialog->SetLayoutAdaptationDone( true );
  return true;
}


wxSizer* wxStandardDialogLayoutAdapter::FindButtonSizer( bool stdButtonSizer, wxDialogHelper* dialog, wxSizer* sizer, int& retBorder, int accumlatedBorder ) {
  for( wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
       node; node = node->GetNext() ) {
    wxSizerItem *item = node->GetData();
    wxSizer *childSizer = item->GetSizer();
    if( childSizer ) {
      int newBorder = accumlatedBorder;
      if( item->GetFlag() & wxALL ) {
        newBorder += item->GetBorder();
      }
      if( stdButtonSizer ) {
        wxStdDialogButtonSizer* buttonSizer = wxDynamicCast( childSizer, wxStdDialogButtonSizer );
        if( buttonSizer ) {
          sizer->Detach( childSizer );
          retBorder = newBorder;
          return buttonSizer;
        }
      } else {
        wxBoxSizer* buttonSizer = wxDynamicCast( childSizer, wxBoxSizer );
        if( buttonSizer && IsOrdinaryButtonSizer( dialog, buttonSizer ) ) {
          sizer->Detach( childSizer );
          retBorder = newBorder;
          return buttonSizer;
        }
      }
      wxSizer* s = FindButtonSizer( stdButtonSizer, dialog, childSizer, retBorder, newBorder );
      if( s ) {
        return s;
      }
    }
  }
  return NULL;
}


bool wxStandardDialogLayoutAdapter::IsOrdinaryButtonSizer( wxDialogHelper* dialog, wxBoxSizer* sizer ) {
  if( sizer->GetOrientation() != wxHORIZONTAL ) {
    return false;
  }
  for( wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
       node; node = node->GetNext() ) {
    wxSizerItem *item = node->GetData();
    wxButton *childButton = wxDynamicCast( item->GetWindow(), wxButton );
    if( childButton && IsStandardButton( dialog, childButton ) ) {
      return true;
    }
  }
  return false;
}


bool wxStandardDialogLayoutAdapter::IsStandardButton( wxDialogHelper* dialog, wxButton* button ) {
  wxWindowID id = button->GetId();
  return ( id == wxID_OK || id == wxID_CANCEL || id == wxID_YES || id == wxID_NO || id == wxID_SAVE ||
           id == wxID_APPLY || id == wxID_HELP || id == wxID_CONTEXT_HELP || dialog->IsUserButtonId( id ) );
}


bool wxStandardDialogLayoutAdapter::FindLooseButtons( wxDialogHelper* dialog, wxStdDialogButtonSizer* buttonSizer, wxSizer* sizer, int& count ) {
  wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
  while( node ) {
    wxSizerItemList::compatibility_iterator next = node->GetNext();
    wxSizerItem *item = node->GetData();
    wxSizer *childSizer = item->GetSizer();
    wxButton *childButton = wxDynamicCast( item->GetWindow(), wxButton );
    if( childButton && IsStandardButton( dialog, childButton ) ) {
      sizer->Detach( childButton );
      buttonSizer->AddButton( childButton );
      count ++;
    }
    if( childSizer ) {
      FindLooseButtons( dialog, buttonSizer, childSizer, count );
    }
    node = next;
  }
  return true;
}

void wxStandardDialogLayoutAdapter::ReparentControls( wxWindow* parent, wxWindow* reparentTo, wxSizer* buttonSizer ) {
  wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst();
  while( node ) {
    wxWindowList::compatibility_iterator next = node->GetNext();
    wxWindow *win = node->GetData();
    if( win != reparentTo && ( !buttonSizer || !buttonSizer->GetItem( win ) ) ) {
      win->Reparent( reparentTo );
    }
    node = next;
  }
}


int wxStandardDialogLayoutAdapter::MustScroll( wxDialog* dialog, wxSize& windowSize, wxSize& displaySize ) {
  wxSize minWindowSize = dialog->GetSizer()->GetMinSize();
  windowSize = dialog->GetSize();
  windowSize = wxSize( wxMax( windowSize.x, minWindowSize.x ), wxMax( windowSize.y, minWindowSize.y ) );
  displaySize = wxDisplay( wxDisplay::GetFromWindow( dialog ) ).GetClientArea().GetSize();
  int flags = 0;
  if( windowSize.y >= ( displaySize.y - wxEXTRA_DIALOG_HEIGHT ) ) {
    flags |= wxVERTICAL;
  }
  if( windowSize.x >= displaySize.x ) {
    flags |= wxHORIZONTAL;
  }
  return flags;
}


bool wxStandardDialogLayoutAdapter::FitWithScrolling( wxDialog* dialog, wxWindowList& windows ) {
  wxSizer* sizer = dialog->GetSizer();
  if( !sizer ) {
    return false;
  }
  sizer->SetSizeHints( dialog );
  wxSize windowSize, displaySize;
  int scrollFlags = MustScroll( dialog, windowSize, displaySize );
  int scrollBarSize = 20;
  if( scrollFlags ) {
    int scrollBarExtraX = 0, scrollBarExtraY = 0;
    bool resizeHorizontally = ( scrollFlags & wxHORIZONTAL ) != 0;
    bool resizeVertically = ( scrollFlags & wxVERTICAL ) != 0;
    if( windows.GetCount() != 0 ) {
      if( ( resizeVertically && !resizeHorizontally ) && ( windowSize.x < ( displaySize.x - scrollBarSize ) ) ) {
        scrollBarExtraX = scrollBarSize;
      }
      if( ( resizeHorizontally && !resizeVertically ) && ( windowSize.y < ( displaySize.y - scrollBarSize ) ) ) {
        scrollBarExtraY = scrollBarSize;
      }
    }
    wxWindowList::compatibility_iterator node = windows.GetFirst();
    while( node ) {
      wxWindow *win = node->GetData();
      wxScrolledWindow* scrolledWindow = wxDynamicCast( win, wxScrolledWindow );
      if( scrolledWindow ) {
        scrolledWindow->SetScrollRate( resizeHorizontally ? 10 : 0, resizeVertically ? 10 : 0 );
        if( scrolledWindow->GetSizer() ) {
          scrolledWindow->GetSizer()->Fit( scrolledWindow );
        }
      }
      node = node->GetNext();
    }
    wxSize limitTo = windowSize + wxSize( scrollBarExtraX, scrollBarExtraY );
    if( resizeVertically ) {
      limitTo.y = displaySize.y - wxEXTRA_DIALOG_HEIGHT;
    }
    if( resizeHorizontally ) {
      limitTo.x = displaySize.x;
    }
    dialog->SetMinSize( limitTo );
    dialog->SetSize( limitTo );
    dialog->SetSizeHints( limitTo.x, limitTo.y, dialog->GetMaxWidth(), dialog->GetMaxHeight() );
  }
  return true;
}


bool wxStandardDialogLayoutAdapter::FitWithScrolling( wxDialog* dialog, wxScrolledWindow* scrolledWindow ) {
  wxWindowList windows;
  windows.Append( scrolledWindow );
  return FitWithScrolling( dialog, windows );
}



class wxDialogLayoutAdapterModule: public wxModule {
    DECLARE_DYNAMIC_CLASS( wxDialogLayoutAdapterModule )
  public:
    wxDialogLayoutAdapterModule() {}
    virtual void OnExit() { delete wxDialogHelper::SetLayoutAdapter( NULL ); }
    virtual bool OnInit() { wxDialogHelper::SetLayoutAdapter( new wxStandardDialogLayoutAdapter ); return true; }
};

IMPLEMENT_DYNAMIC_CLASS( wxDialogLayoutAdapterModule, wxModule )



IMPLEMENT_CLASS( wxScrollingDialog, wxDialog )

void wxScrollingDialog::Init() {
  wxDialogHelper::SetDialog( this );
}

bool wxScrollingDialog::Create( wxWindow *parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) {
  return wxDialog::Create( parent, id, title, pos, size, style, name );
}


bool wxScrollingDialog::Show( bool show ) {
  if( CanDoLayoutAdaptation() ) {
    DoLayoutAdaptation();
  }
  return wxDialog::Show( show );
}


int wxScrollingDialog::ShowModal() {
  if( CanDoLayoutAdaptation() ) {
    DoLayoutAdaptation();
  }
  return wxDialog::ShowModal();
}



IMPLEMENT_DYNAMIC_CLASS( wxScrollingPropertySheetDialog, wxPropertySheetDialog )

void wxScrollingPropertySheetDialog::Init() {
  wxDialogHelper::SetDialog( this );
}


wxWindow* wxScrollingPropertySheetDialog::GetContentWindow() const {
  return GetBookCtrl();
}


bool wxScrollingPropertySheetDialog::Show( bool show ) {
  if( CanDoLayoutAdaptation() ) {
    DoLayoutAdaptation();
  }
  return wxPropertySheetDialog::Show( show );
}


int wxScrollingPropertySheetDialog::ShowModal() {
  if( CanDoLayoutAdaptation() ) {
    DoLayoutAdaptation();
  }
  return wxPropertySheetDialog::ShowModal();
}

